/*
 * Copyright (c) 2021 HiSilicon (Shanghai) Technologies CO., LIMITED.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <signal.h>
#include <stdint.h>
#include <termios.h>
#include <sys/time.h>
#include <linux/limits.h>

#include "hi_app_base.h"
#include "inet_test_util.h"
#include "strm_msg_sk.h"
#include "cjson_help.h"
#include "lite_httpd.h"
#include "aic_mng.h"
#include "mpp_sess.h"

#include "httpd_aic.h"

/**
    HTTP interface URLs.
*/
#define URL_SSE_EVT         "/aic/sse_evt"
#define URL_GET_INFO        "/aic/get_info"
#define URL_GET_STATUS      "/aic/get_status"
#define URL_START_VI        "/aic/start_vi"
#define URL_STOP_VI         "/aic/stop_vi"
#define URL_START_VO        "/aic/start_vo"
#define URL_STOP_VO         "/aic/stop_vo"
#define URL_STOP_VIOS       "/aic/stop_vios"
#define URL_SET_AI_PLUG     "/aic/set_ai_plug"
#define URL_LIST_AI_PLUG    "/aic/list_ai_plug"
#define URL_POST_PIC        "/aic/post_pic"
#define URL_POST_MODEL      "/aic/post_model"
#define URL_UPDATE_SOFT     "/aic/update_soft"

/**
    StrmMsg server port.
*/
#define STRM_SVR_PORT       8601 // StrmMsgSk TCP server port
#define STRM_SVRS_PORT      8602 // StrmMsgSk sec TCP server port

/**
    update software常量.
*/
#define UPDATE_SOFT_DIR     "/update" // 存放升级包的目录
#define UPDATE_SOFT_EXT     ".tar.gz" // 升级包文件名后缀
#define UPDATE_SOFT_INFO    UPDATE_SOFT_DIR "/update.info" // 升级包信息文件
#define UPDATE_WAIT_TIME    1000 // 接收完成后等待的时间，暂时使用

/**
    事件相关常量.
*/
#define ENAME_AI_RES        "aiRes" // AI计算结果事件名

/**
    其他常量.
*/
#define ERR_RSP_BODY        "{ \"ret\": -1 }" // 错误响应body

/**
    SseNode.
*/
typedef struct SseNode {
    struct list_head lnode;
    SseReq *sse;
}   SseNode;

/**
    httpd aic.
*/
typedef struct HttpAic {
    SkPair httpEvtChn; // 接收HttpdEvt的channel
    int svcThrdNum; // 正在运行的后台服务线程数目
    HttpReq *postPicReq; // 正在接受host post pic的请求
    int strmTcpSvr; // 监听StrmMsgSk的TCP server fd
    StrmMsgSk *strmMsgSk; // 与host交互pic, json的消息端点
    struct list_head sseSessList; // SseNode list, 应替换为STL std::set

    // aic stream info
    PAYLOAD_TYPE_E codecType; // stream codec type, PT_BUTT表示当前为stream
    int width; // stream width, -1表示不确定，是可以的
    int height; // stream height, -1表示不确定，是可以的
}   HttpAic;

/**
    HttpAic全局对象.
*/
static HttpAic g_haic = {
    .httpEvtChn = {
        .in = -1,
        .out = -1
    },
    .svcThrdNum = 0,
    .postPicReq = NULL,
    .strmTcpSvr = -1,
    .strmMsgSk = NULL,
    .sseSessList = LIST_HEAD_INIT(g_haic.sseSessList),

    .codecType = PT_BUTT,
    .width = -1,
    .height = -1,
};

/**
    更新模型处理线程.
*/
static void* PostModelThrd(void *user)
{
    HttpReq *req = (HttpReq*)user;
    const char *rspStatus = "500";
    char filePath[NORM_BUF_SIZE] = "?";
    MemBlk *blk = NULL;
    FILE *file = NULL;
    int ret;
    char stFilePath[PATH_MAX] = {0};

    LOGI("HAIC: proc post model ...\n");

    const char* hval = HttpHdrsFind(HttpReqReqHdrs(req), "filename", NULL);
    HI_EXP_CMD_GOTO(!hval || !*hval, (rspStatus = "500"), END, "on post model without 'filename' hdr\n");

    if (snprintf_s(filePath, sizeof(filePath), sizeof(filePath) - 1, "./plugs/%s", hval) < 0) {
        HI_ASSERT(0);
    }

    if (!realpath(filePath, stFilePath)) {
        LOGE("PostModelThrd filePath (%s) is not exit\n", filePath);
        HI_ASSERT(0);
    }
    LOGI("PostModelThrd standard file path:%s\n", stFilePath);

    file = fopen(stFilePath, "wb");
    HI_EXP_CMD_GOTO(!file, (rspStatus = "500"), END,
        "open '%s' for write FAIL, err='%s, %d'\n", stFilePath, strerror(errno), errno);

    // read file blocks and write to fileone by one
    while ((ret = HttpReqReadBody(req, &blk, false)) > 0) {
        HI_ASSERT(blk);
        int res = fwrite(blk->data, 1, blk->len, file);
        HI_EXP_CMD_GOTO(res != blk->len, (rspStatus = "500"), END,
            "write '%s' FAIL, err='%s, %d'\n", stFilePath, strerror(errno), errno);
        MemBlkDelete(blk); // 先判断是否写入成功，再指针free，防止使用已经释放的资源
    }
    HI_EXP_CMD_GOTO(ret < 0, (rspStatus = "500"), END, "HttpReqReadBody FAIL, ret=%d\n", ret);
    rspStatus = "200";

    END:
        HttpReqSimpleRsp(req, rspStatus);
        HttpReqDelRef(req);
        if (file) {
            fclose(file);
        }
        LOGI("HAIC: proc post model done, name='%s', res='%s'\n", stFilePath, rspStatus);
        __sync_sub_and_fetch(&g_haic.svcThrdNum, 1);
        return NULL;
}

/**
    升级软件处理线程.
*/
static void* UpdateSoftThrd(void *user)
{
    HttpReq *req = (HttpReq*)user;
    const char *rspStatus = "500";
    char filePath[PATH_MAX] = "?";
    FILE *infoFile = NULL; // update info file
    int res;

    LOGI("HAIC: proc update soft ...\n");

    const char* fileName = HttpHdrsFind(HttpReqReqHdrs(req), "filename", NULL);
    HI_EXP_CMD_GOTO((!fileName || !*fileName), (rspStatus = "500"), END,
        "update soft without 'filename' hdr\n");

    // 判断文件名后缀是否为".tar.gz"，目前仅支持这种格式
    int nameLen = strlen(fileName);
    int extLen = strlen(UPDATE_SOFT_EXT);
    if (nameLen <= extLen || strcmp(&fileName[nameLen - extLen], UPDATE_SOFT_EXT) != 0) {
        LOGE("update soft FAIL, for '%s' not ext by '%s'\n", fileName, UPDATE_SOFT_EXT);
        rspStatus = "500";
        goto END;
    }

    // 需要时创建目录
    res = MkdirIfNeed(UPDATE_SOFT_DIR, true);
    HI_EXP_CMD_GOTO(res < 0, (rspStatus = "500"), END, "mkdir '%s' FAIL\n", UPDATE_SOFT_EXT);

    // 接收并保存文件
    if (snprintf_s(filePath, sizeof(filePath), sizeof(filePath) - 1, "%s/%s", UPDATE_SOFT_DIR, fileName) < 0) {
        HI_ASSERT(0);
    }
    res = HttpdPutFile(req, filePath, false);
    HI_EXP_CMD_GOTO(res < 0, (rspStatus = "500"), END, "get-save file FAIL, ret=%d\n", res);

    // 将文件名添加到update信息文件中
    infoFile = fopen(UPDATE_SOFT_INFO, "wb");
    HI_EXP_CMD_GOTO(!infoFile, (rspStatus = "500"), END,
        "open '%s' for 'wb' FAIL, err='%s, %d'\n", filePath, strerror(errno), errno);
    res = fprintf(infoFile, "%s\n", filePath); // 直接给全路径
    HI_EXP_CMD_GOTO(res < 0, (rspStatus = "500"), END,
        "write '%s' FAIL, err='%s, %d'\n", filePath, strerror(errno), errno);
    rspStatus = "200";

    END:
        HttpReqSimpleRsp(req, rspStatus);
        HttpReqDelRef(req);

        if (infoFile) {
            fclose(infoFile);
        }
        if (strcmp(rspStatus, "200") != 0) { // FAIL
            LOGW("remove '%s' for proc FAIL\n", filePath);
            remove(filePath); // 有可能失败
        }

        LOGI("HAIC: proc update soft done, name='%s', rsp='%s'\n", filePath, rspStatus);
        __sync_sub_and_fetch(&g_haic.svcThrdNum, 1);

        // 若成功，则请求重启系统
        if (strcmp(rspStatus, "200") == 0) { // OK
            usleep(UPDATE_WAIT_TIME * HI_USLEEP_MS);
            RebootSystem(-1);
        }
        return NULL;
}

/**
    设置插件处理线程.
*/
static void* SetAiPlugThrd(void* param)
{
    RapiReq *req = (HttpReq*)param;
    const char *inData = NULL;
    int inLen;
    cJSON *json = NULL;
    const char *uuid = NULL;
    AiPlugLib plug;

    inData = RapiReqInData(req, &inLen);
    HI_EXP_GOTO(!inData, END, "set_ai_plug without req-body\n");
    LOGI("HAIC: on set_ai_plug, reqLen=%d, reqData=\n", inLen);
    DOLOGI(HiDumpTxt(inData, inLen));

    json = cJSON_Parse(inData);
    HI_EXP_GOTO(!json, END, "OnSetAiPlug FAIL, for JSON parse FAIL\n");
    uuid = JsonStr(json, "uuid", "");

    LOGI("HAIC: call AicSetAiPlug(%s) ...\n", (*uuid ? uuid : "null"));
    AicSetAiPlug(uuid, &plug, false);

    END:
        if (json) {
            cJSON_Delete(json);
        }
        RapiReqReply(req, ERR_RSP_BODY, sizeof(ERR_RSP_BODY) - 1);
        HttpReqDelRef(req);

        if (__sync_sub_and_fetch(&g_haic.svcThrdNum, 1) < 0) {
            HI_ASSERT(0);
        }
        return NULL;
}

/**
    关闭sse并从list中删除.
*/
static void CloseSseNode(SseNode* node, bool closeSse)
{
    EmDelFd(MainEvtMon(), HttpReqCloseFd(node->sse));
    list_del(&node->lnode);

    AicUnsubsAiSvc((uintptr_t)node, NULL);
    AicUnsubsEvt((uintptr_t)node, NULL);

    if (closeSse) {
        SseReqClose(node->sse, 0);
    }
    HttpReqDelRef(node->sse);

    free(node);
}

/**
    AIC终止stream通知.
*/
static void OnStrmOff(void* user)
{
    LOGI("HAIC: on strm off\n");
    HttpAic *self = (HttpAic*)user;
    HI_ASSERT(self == &g_haic);

    self->codecType = PT_BUTT;
    self->width = -1;
    self->height = -1;
}

/**
    AIC启动stream通知.
*/
static bool OnStrmOn(void* user, int vencChn, PAYLOAD_TYPE_E codecType, int width, int height)
{
    LOGI("HAIC: on strm on, codec=%d, width=%d, height=%d\n", codecType, width, height);
    HttpAic *self = (HttpAic*)user;
    HI_ASSERT(self == &g_haic);

    if (self->codecType != PT_BUTT) {
        LOGW("onStrmOn without onStrmOff before\n");
        OnStrmOff(user);
    }

    self->codecType = codecType;
    self->width = width;
    self->height = height;
    return (codecType == PT_JPEG || codecType == PT_MJPEG) ? true : false;
}

/**
    收到AIC提交的frame.
*/
static void OnVencFrm(void* user, VencFrm* frm)
{
    HI_ASSERT(frm);
    HttpAic *self = (HttpAic*)user;
    HI_ASSERT(self == &g_haic);

    if (!self->strmMsgSk) {
        LOGW("tx pic discard, for no strm sk\n");
        return;
    }

    // 平面化frame
    MemBlk *pic = VencFrmToMblk(&frm->strm, NULL, sizeof(uint32_t));
    if (!pic) {
        return;
    }

    // 添加消息长度header
    HI_ASSERT(pic->offset >= sizeof(uint32_t));
    pic->offset -= sizeof(uint32_t);
    HiPutBe32(pic->size - pic->offset, &pic->data[pic->offset]);

    int ret = SmsSend(self->strmMsgSk, pic);
    if (ret != 1) {
        MemBlkDelete(pic);
    }
}

/**
    实现的IAicStrmUser接口.
*/
static const IAicStrmUser G_HAIC_STRM_USER = {
    .OnStrmOn = OnStrmOn,
    .OnStrmOff = OnStrmOff,
    .OnVencFrm = OnVencFrm,
    .OnVideoFrm = NULL,
};

/**
    收齐post pic的数据.
*/
static void OnPostPicRead(void* user, int fd, uint32_t evts)
{
    HI_ASSERT(g_haic.postPicReq);
    int ret;
    ret = HttpReqReadFd(g_haic.postPicReq);
    HI_ASSERT(fd == ret);
    EvtChkRet(evts, FDE_IN, fd);
    MemBlk *data = NULL;

    EmDelFd(MainEvtMon(), fd);

    ret = HttpReqReadBody(g_haic.postPicReq, &data, true);
    if (ret <= 0) { // 请求被对端close/reset
        LOGW("read PIC POST FAIL, ret=%d\n", ret);
        HttpReqDelRef(g_haic.postPicReq);
        g_haic.postPicReq = NULL;
        return;
    }
    int res = HttpReqReqBodyEmpty(g_haic.postPicReq);
    HI_ASSERT(res);

    // 先回复client，再处理图片
    HttpReqSimpleRsp(g_haic.postPicReq, ret > 0 ? "200" : "500");
    HttpReqDelRef(g_haic.postPicReq);
    g_haic.postPicReq = NULL;

    AicSubmitJpg(data->data, data->len, false);
    MemBlkDelete(data);
}

/**
    AIC事件处理.
    目前简化实现为向所有SseSess发送事件.
*/
static void OnAicEvt(void* user, uint32_t cltId, const char* evtName, const char* evtJson, int evtLen)
{
    bool res = IsMainThrd();
    HI_ASSERT(res);
    SseNode *node = (SseNode*)user;
    HI_ASSERT((uintptr_t)node == cltId);

    // 直接透传evtJson
    int ret = SseReqPush(node->sse, -1, evtName, evtJson, evtLen);
    HI_EXP_LOGE(ret < 0, "sse push aic evt FAIL, ret=%d\n", ret);
}

/**
    AI计算结果事件处理.
    目前简化实现为向所有SseSess发送事件.
*/
static void OnAiRes(void* user, uintptr_t cltId, const char* plugUuid, const char* resJson, int resLen)
{
    bool res = IsMainThrd();
    HI_ASSERT(res);
    SseNode *node = (SseNode*)user;
    HI_ASSERT((uintptr_t)node == cltId);

    int size = resLen + strlen(plugUuid) + NORM_BUF_SIZE;
    char *buf = (char*)malloc(size);
    HI_ASSERT(buf);

    // 将resJson包到更大的json中
    int len = snprintf_s(buf, size, size - 1, "{ \"plugUuid\": \"%s\", \"aiRes\": %s }", plugUuid, resJson);
    HI_ASSERT(len > 0);

    HI_ASSERT(node->sse);
    int ret = SseReqPush(node->sse, -1, ENAME_AI_RES, buf, len);
    free(buf);
    HI_EXP_LOGE(ret < 0, "sse push ai res FAIL, ret=%d\n", ret);
}

/**
    监视到sse closed.
*/
static void OnSseDisc(void* user, int fd, uint32_t evts)
{
    LOGI("HAIC: on sse disc ...\n");
    SseNode *node = (SseNode*)user;
    HI_ASSERT(node);
    int ret = HttpReqCloseFd(node->sse);
    HI_ASSERT(ret == fd);
    EvtChkRet(evts, FDE_IN, fd);

    CloseSseNode(node, false);
}

/**
    收到client的sse create请求.
*/
static int OnSseConn(void* user, SseReq* req)
{
    LOGI("HAIC: on sse conn ...\n");
    bool res = IsMainThrd();
    HI_ASSERT(res);
    int ret;

    ret = SseReqReply(req, 0);
    if (ret < 0) {
        LOGE("sse reply FAIL, ret=%d\n", ret);
        HttpReqDelRef(req);
        return ret;
    }

    SseNode *node = (SseNode*)malloc(sizeof(*node));
    HI_ASSERT(node);
    INIT_LIST_HEAD(&node->lnode);
    node->sse = req;
    list_add_tail(&node->lnode, &g_haic.sseSessList);

    ret = AicSubsAiSvc((uintptr_t)node, NULL, OnAiRes, node);
    HI_EXP_LOGE(ret < 0, "AicSubsAiSvc FAIL, ret=%d\n", ret);

    ret = AicSubsEvt((uintptr_t)node, NULL, OnAicEvt, node);
    HI_EXP_LOGE(ret < 0, "AicSubsEvt FAIL, ret=%d\n", ret);

    if (EmAddFd(MainEvtMon(), HttpReqCloseFd(req), FDE_IN, OnSseDisc, node) < 0) {
        HI_ASSERT(0);
    }
    return 0;
}

/**
    收到client的get_info请求.
*/
static int OnGetInfo(void* user, HttpReq* req)
{
    int res = IsMainThrd();
    HI_ASSERT(res);
    int ret = 0;

    char outBuf[LARGE_BUF_SIZE];
    int outLen = snprintf_s(outBuf, sizeof(outBuf), sizeof(outBuf) - 1,
        "{ \"ret\": %d, " "\"info\": { \"appName\": \"%s\", \"appVer\": \"%s\", \"sns0Name\": \"%s\" }" "}",
        ret, APP_NAME, APP_VER, CurSnsType(0, NULL));
    RapiReqReply(req, outBuf, outLen);
    HttpReqDelRef(req);
    return 0;
}

/**
    收到client的get_status请求.
*/
static int OnGetStatus(void* user, HttpReq* req)
{
    LOGI("HAIC: call AicGetStatus() ...\n");
    bool res = IsMainThrd();
    HI_ASSERT(res);
    MemBlk *reply = MemBlkNew2(HUGE_BUF_SIZE, TINY_BUF_SIZE); // ensure by xiao
    HI_ASSERT(reply);

    int ret = AicGetStatus(reply);
    if (ret < 0) {
        MemBlkDelete(reply);
        RapiReqReply(req, ERR_RSP_BODY, sizeof(ERR_RSP_BODY) - 1);
    } else {
        HI_ASSERT(reply->len > 0);
        MemBlkPrefixStr(reply, "{ \"ret\": 0, \"status\": ", -1);
        MemBlkSuffixStr(reply, "\n}", -1);
        RapiReqReply2(req, reply);
    }

    HttpReqDelRef(req);

    return 0;
}

/**
    收到client的start_vi请求.
*/
static int OnStartVi(void* user, RapiReq* req)
{
    bool res = IsMainThrd();
    HI_ASSERT(res);
    const char *inData = NULL;
    int inLen;
    cJSON *json = NULL;
    int ret = -1;

    inData = RapiReqInData(req, &inLen);
    HI_EXP_GOTO(!inData, END, "start_vi without req-body\n");
    DOLOGI((printf("HAIC: OnStartVi, reqLen=%d, reqData=\n", inLen),
        HiDumpTxt(inData, inLen)));

    json = cJSON_Parse(inData);
    HI_EXP_GOTO(!json, END, "parse JSON FAIL\n");

    int viType = JsonInt(json, "viType", -1);
    int outWidth = JsonInt(json, "outWidth", -1);
    int outHeight = JsonInt(json, "outHeight", -1);
    if ((viType < 0 || viType >= AIC_VIT_BUTT) || outWidth <= 0 || outHeight <= 0) {
        LOGE("param err\n");
        goto END;
    }

    LOGI("HAIC: call AicStartVi(%d, true, %d, %d, -1) ...\n", viType, outWidth, outHeight);
    ret = AicStartVi(viType, true, outWidth, outHeight, -1);

    END:
        if (json) {
            cJSON_Delete(json);
        }

        char outBuf[NORM_BUF_SIZE];
        int outLen = snprintf_s(outBuf, sizeof(outBuf), sizeof(outBuf) - 1, "{ \"ret\": %d }", ret);
        RapiReqReply(req, outBuf, outLen);
        HttpReqDelRef(req);
        return 0;
}

/**
    收到client的stop_vi请求.
*/
static int OnStopVi(void* user, RapiReq* req)
{
    bool res = IsMainThrd();
    HI_ASSERT(res);
    const char *inData = NULL;
    int inLen;
    cJSON *json = NULL;
    int ret = -1;

    inData = RapiReqInData(req, &inLen);
    HI_EXP_GOTO(!inData, END, "stop_vi without req-body\n");
    DOLOGI((printf("HAIC: OnStopVi, reqLen=%d, reqData=\n", inLen),
        HiDumpTxt(inData, inLen)));

    json = cJSON_Parse(inData);
    HI_EXP_GOTO(!json, END, "parse JSON FAIL\n");

    int viType = JsonInt(json, "viType", -1);
    if ((viType < 0 || viType >= AIC_VIT_BUTT)) {
        LOGE("param err\n");
        goto END;
    }

    LOGI("HAIC: call AicStopVi(%d) ...\n", viType);
    ret = AicStopVi(viType, true);

    END:
        if (json) {
            cJSON_Delete(json);
        }

        char outBuf[NORM_BUF_SIZE];
        int outLen = snprintf_s(outBuf, sizeof(outBuf), sizeof(outBuf) - 1, "{ \"ret\": %d }", ret);
        RapiReqReply(req, outBuf, outLen);
        HttpReqDelRef(req);
        return 0;
}

/**
    收到client的start_vo请求.
*/
static int OnStartVo(void* user, RapiReq* req)
{
    bool res = IsMainThrd();
    HI_ASSERT(res);
    const char *inData = NULL;
    int inLen;
    cJSON *json = NULL;
    int ret = -1;

    inData = RapiReqInData(req, &inLen);
    HI_EXP_GOTO(!inData, END, "start_vo without req-body\n");
    DOLOGI((printf("HAIC: OnStopVo, reqLen=%d, reqData=\n", inLen),
        HiDumpTxt(inData, inLen)));

    json = cJSON_Parse(inData);
    HI_EXP_GOTO(!json, END, "parse JSON FAIL\n");

    int voType = JsonInt(json, "voType", -1);
    int outWidth = JsonInt(json, "outWidth", -1);
    int outHeight = JsonInt(json, "outHeight", -1);
    // 目前仅支持host start/stop type IP
    if (voType != AIC_VOT_IP || outWidth <= 0 || outHeight <= 0) {
        LOGE("param err\n");
        goto END;
    }

    LOGI("HAIC: call AicStartVo(%d, %d, %d, MJPEG) ...\n", voType, outWidth, outHeight);
    ret = AicStartVo(voType, outWidth, outHeight, PT_MJPEG);
    HI_CHK_GOTO(ret, END, "start vo FAIL\n");

    ret = (int)AicAddStrmUser(&G_HAIC_STRM_USER, &g_haic);
    HI_ASSERT(ret);

    END:
        if (json) {
            cJSON_Delete(json);
        }

        char outBuf[NORM_BUF_SIZE];
        int outLen = snprintf_s(outBuf, sizeof(outBuf), sizeof(outBuf) - 1, "{ \"ret\": %d }", ret);
        RapiReqReply(req, outBuf, outLen);
        HttpReqDelRef(req);
        return 0;
}

/**
    收到client的stop_vo请求.
*/
static int OnStopVo(void* user, RapiReq* req)
{
    bool res = IsMainThrd();
    HI_ASSERT(res);
    const char *inData = NULL;
    int inLen;
    cJSON *json = NULL;
    int ret = -1;

    inData = RapiReqInData(req, &inLen);
    HI_EXP_GOTO(!inData, END, "stop_vo without req-body\n");
    DOLOGI((printf("HAIC: OnStopVo, reqLen=%d, reqData=\n", inLen),
        HiDumpTxt(inData, inLen)));

    json = cJSON_Parse(inData);
    HI_EXP_GOTO(!json, END, "parse JSON FAIL\n");

    int voType = JsonInt(json, "voType", -1);
    // 目前仅支持host start/stop type IP
    HI_EXP_GOTO(voType != AIC_VOT_IP, END, "param err\n");

    LOGI("HAIC: call AicStopVo(%d) ...\n", voType);
    AicDelStrmUser(&G_HAIC_STRM_USER, &g_haic);
    ret = AicStopVo(voType);

    END:
        if (json) {
            cJSON_Delete(json);
        }

        char outBuf[NORM_BUF_SIZE];
        int outLen = snprintf_s(outBuf, sizeof(outBuf), sizeof(outBuf) - 1, "{ \"ret\": %d }", ret);
        RapiReqReply(req, outBuf, outLen);
        HttpReqDelRef(req);
        return 0;
}

/**
    收到client的stop_vios请求.
*/
static int OnStopVios(void* user, RapiReq* req)
{
    bool res = IsMainThrd();
    HI_ASSERT(res);
    int ret;

    LOGI("HAIC: call AicStopVios ...\n");
    AicDelStrmUser(&G_HAIC_STRM_USER, &g_haic);
    ret = AicStopVios(true);

    char outBuf[NORM_BUF_SIZE];
    int outLen = snprintf_s(outBuf, sizeof(outBuf), sizeof(outBuf) - 1, "{ \"ret\": %d }", ret);
    RapiReqReply(req, outBuf, outLen);
    HttpReqDelRef(req);
    return 0;
}

/**
    收到client的set_ai_plug请求.
*/
static int OnSetAiPlug(void* user, RapiReq* req)
{
    bool res = IsMainThrd();
    HI_ASSERT(res);
    pthread_t thrdId;
    int ret;

    __sync_add_and_fetch(&g_haic.svcThrdNum, 1);
    ret = DetachThreadCreate(&thrdId, SetAiPlugThrd, req);
    if (ret < 0) {
        LOGE("pthread_create FAIL, err='%s (%d)'\n", strerror(errno), errno);
        if (__sync_sub_and_fetch(&g_haic.svcThrdNum, 1) < 0) {
            HI_ASSERT(0);
        }
        HI_ASSERT(0);
        HttpReqReset(req, 0);
        HttpReqDelRef(req);
    }
    return 0;
}

/**
    收到client的list_ai_plug请求.
*/
static int OnListAiPlug(void* user, RapiReq* req)
{
    LOGI("HAIC: call AicListAiPlugs() ...\n");
    bool res = IsMainThrd();
    HI_ASSERT(res);
    MemBlk *reply = NULL;

    int ret = AicListAiPlugs(&reply, TINY_BUF_SIZE);
    if (ret < 0) {
        RapiReqReply(req, ERR_RSP_BODY, sizeof(ERR_RSP_BODY) - 1);
    } else {
        char buf[SMALL_BUF_SIZE];
        int len = snprintf_s(buf, sizeof(buf), sizeof(buf) - 1, "{ \"ret\": %d, \"plugs\": ", ret);
        HI_ASSERT(len > 0);
        MemBlkPrefixStr(reply, buf, len);
        MemBlkSuffixStr(reply, "\n}", -1);
        RapiReqReply2(req, reply);
    }

    HttpReqDelRef(req);
    return 0;
}

/**
    收到client的aic_post_pic请求.
*/
static int OnPostPic(void* user, HttpReq* req)
{
    bool ret = IsMainThrd();
    HI_ASSERT(ret);

    if (g_haic.postPicReq) {
        LOGW("discard pic POST, for post exist\n");
        HttpReqReset(req, 0);
        HttpReqDelRef(req);
        return 0;
    }
    g_haic.postPicReq = req;

    int fd = HttpReqReadFd(req);
    if (EmAddFd(MainEvtMon(), fd, FDE_IN, OnPostPicRead, NULL) < 0) {
        HI_ASSERT(0);
    }
    return 0;
}

/**
    收到client的aic_post_model请求.
*/
static int OnPostModel(void* user, HttpReq* req)
{
    bool res = IsMainThrd();
    HI_ASSERT(res);
    pthread_t thrdId;
    int ret;

    __sync_add_and_fetch(&g_haic.svcThrdNum, 1);
    ret = DetachThreadCreate(&thrdId, PostModelThrd, req);
    if (ret < 0) {
        LOGE("pthread_create FAIL, err='%s (%d)'\n", strerror(errno), errno);
        if (__sync_sub_and_fetch(&g_haic.svcThrdNum, 1) < 0) {
            HI_ASSERT(0);
        }
        HI_ASSERT(0);
        HttpReqReset(req, 0);
        HttpReqDelRef(req);
    }
    return 0;
}

/**
    收到client的aic_update_soft请求.
*/
static int OnUpdateSoft(void* user, HttpReq* req)
{
    bool res = IsMainThrd();
    HI_ASSERT(res);
    pthread_t thrdId;
    int ret;

    __sync_add_and_fetch(&g_haic.svcThrdNum, 1);
    ret = DetachThreadCreate(&thrdId, UpdateSoftThrd, req);
    if (ret < 0) {
        LOGE("pthread_create FAIL, err='%s (%d)'\n", strerror(errno), errno);
        if (__sync_sub_and_fetch(&g_haic.svcThrdNum, 1) < 0) {
            HI_ASSERT(0);
        }
        HI_ASSERT(0);
        HttpReqReset(req, 0);
        HttpReqDelRef(req);
    }
    return 0;
}

/**
    StrmMsgSk监听到消息到达.
*/
static void OnStrmSkRx(void* user, int fd, uint32_t evts)
{
    HI_ASSERT(g_haic.strmMsgSk);
    int ret;
    ret = SmsRecvFd(g_haic.strmMsgSk);
    HI_ASSERT(fd == ret);
    EvtChkRet(evts, FDE_IN, fd);
    MemBlk *msg = NULL;

    ret = SmsRecv(g_haic.strmMsgSk, &msg);
    if (ret == 1) {
        HI_ASSERT(msg && msg->size > sizeof(uint32_t));
        AicSubmitJpg(&msg->data[sizeof(uint32_t)], msg->size - sizeof(uint32_t), false);
        MemBlkDelete(msg);
    } else if (ret == 0) {
        LOGW("detect strmMsgSk closed\n");
        SmsDestroy(g_haic.strmMsgSk);
        g_haic.strmMsgSk = NULL;
    } else { // ret < 0
        HI_ASSERT(0); // 不应该出现
    }
}

/**
    StrmMsgSk server检测到TCP连接到达.
*/
static void OnStrmSvrConn(void* user, int fd, uint32_t evts)
{
    HI_ASSERT(fd == g_haic.strmTcpSvr);
    EvtChkRet(evts, FDE_IN, fd);

    if (g_haic.strmMsgSk) {
        EmDelFd(MainEvtMon(), SmsRecvFd(g_haic.strmMsgSk));
        SmsDestroy(g_haic.strmMsgSk);
        g_haic.strmMsgSk = NULL;
    }

    int sock = SockAccept(fd);
    if (sock < 0 || SmsCreate(&g_haic.strmMsgSk, sock, 0, 0) < 0) {
        HI_ASSERT(0);
        return;
    }
    if (EmAddFd(MainEvtMon(),
        SmsRecvFd(g_haic.strmMsgSk), FDE_IN, OnStrmSkRx, NULL) < 0) {
        HI_ASSERT(0);
    }
}

/**
    初始化.
*/
int HttpdAicInit(void)
{
    bool res = IsMainThrd();
    HI_ASSERT(res);
    res = SkPairValid(&g_haic.httpEvtChn);
    HI_ASSERT(!res);
    int ret;
    ret = SkPairCreate(&g_haic.httpEvtChn);
    HI_ASSERT(ret >= 0);
    HttpdMonChn(g_haic.httpEvtChn, NULL);

    g_haic.strmTcpSvr = TcpSvrCreate(STRM_SVR_PORT, 0, true, -1);
    HI_ASSERT(g_haic.strmTcpSvr >= 0);
    if (EmAddFd(MainEvtMon(), g_haic.strmTcpSvr, FDE_IN, OnStrmSvrConn, NULL) < 0) {
        HI_ASSERT(0);
    }

    HttpdRegSse(URL_SSE_EVT, OnSseConn, NULL, g_haic.httpEvtChn);
    HttpdRegRapi(URL_GET_INFO, OnGetInfo, NULL, g_haic.httpEvtChn);
    HttpdRegRapi(URL_GET_STATUS, OnGetStatus, NULL, g_haic.httpEvtChn);
    HttpdRegRapi(URL_START_VI, OnStartVi, NULL, g_haic.httpEvtChn);
    HttpdRegRapi(URL_STOP_VI, OnStopVi, NULL, g_haic.httpEvtChn);
    HttpdRegRapi(URL_START_VO, OnStartVo, NULL, g_haic.httpEvtChn);
    HttpdRegRapi(URL_STOP_VO, OnStopVo, NULL, g_haic.httpEvtChn);
    HttpdRegRapi(URL_STOP_VIOS, OnStopVios, NULL, g_haic.httpEvtChn);
    HttpdRegRapi(URL_SET_AI_PLUG, OnSetAiPlug, NULL, g_haic.httpEvtChn);
    HttpdRegRapi(URL_LIST_AI_PLUG, OnListAiPlug, NULL, g_haic.httpEvtChn);
    HttpdRegHttp(URL_POST_PIC, "POST", OnPostPic, NULL, g_haic.httpEvtChn, HRF_REQ_BODY_ONE);
    HttpdRegHttp(URL_POST_MODEL, "POST", OnPostModel, NULL, g_haic.httpEvtChn, 0);
    HttpdRegHttp(URL_UPDATE_SOFT, "POST", OnUpdateSoft, NULL, g_haic.httpEvtChn, 0);
    return 0;
}

/**
    去初始化.
*/
int HttpdAicExit(void)
{
    static const int sleepPerLoop = 10; // 每次循环sleep的ms数
    bool ret = IsMainThrd();
    HI_ASSERT(ret);

    HttpdUnreg(URL_SSE_EVT);
    HttpdUnreg(URL_GET_INFO);
    HttpdUnreg(URL_GET_STATUS);
    HttpdUnreg(URL_START_VI);
    HttpdUnreg(URL_STOP_VI);
    HttpdUnreg(URL_START_VO);
    HttpdUnreg(URL_STOP_VO);
    HttpdUnreg(URL_SET_AI_PLUG);
    HttpdUnreg(URL_LIST_AI_PLUG);
    HttpdUnreg(URL_POST_PIC);
    HttpdUnreg(URL_POST_MODEL);
    HttpdUnreg(URL_UPDATE_SOFT);

    if (g_haic.strmMsgSk) {
        SmsDestroy(g_haic.strmMsgSk);
        g_haic.strmMsgSk = NULL;
    }
    if (g_haic.strmTcpSvr >= 0) { // NOTE: 此前create可能会失败
        close(g_haic.strmTcpSvr);
        g_haic.strmTcpSvr = -1;
    }

    if (g_haic.postPicReq) {
        HttpReqReset(g_haic.postPicReq, 0);
        HttpReqDelRef(g_haic.postPicReq);
        g_haic.postPicReq = NULL;
    }

    while (!list_empty(&g_haic.sseSessList)) {
        SseNode *node = list_entry(g_haic.sseSessList.next, SseNode, lnode);
        CloseSseNode(node, true);
    }

    SkPairValid(&g_haic.httpEvtChn);
    HttpdUnmonChn(g_haic.httpEvtChn, NULL);

    // 等待所有后台线程结束
    if (g_haic.svcThrdNum > 0) {
        LOGI("HAIC: wait svc thrd end, num=%d, ...\n", g_haic.svcThrdNum);
        while (g_haic.svcThrdNum > 0) {
            usleep(((long)sleepPerLoop) * HI_USLEEP_MS);
        }
        usleep(((long)sleepPerLoop) * HI_USLEEP_MS); // 尽量让后台线程被OS销毁，但不是必须
        LOGI("HAIC: wait svc thrd end done\n");
    }

    // 清空httpdEvtChn中未被处理的消息
    HttpdClearChn(g_haic.httpEvtChn);
    SkPairDestroy(&g_haic.httpEvtChn);
    return 0;
}

